package com.xframe.xdal.core.util;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.*;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.xframe.xdal.core.constant.ErrorCode;
import com.xframe.xdal.core.exception.DalFrameException;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 字符串工具类
 *
 * @author xufeng
 */
public class StringUtil extends StringUtils {
    /**
     * 空字符串
     */
    private static final String NULLSTR = "";

    /**
     * 下划线
     */
    private static final String SEPARATOR = "_";



    /**
     * 检查值是否为null,如果为null返回true,不为null返回false
     *
     * @param obj
     * @return
     */
    public static boolean isValueNull(Object... obj) {
        for (int i = 0; i < obj.length; i++) {
            if (obj[i] == null || "".equals(obj[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取参数不为空值
     *
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue) {
        return value != null ? value : defaultValue;
    }


    /**
     * * 判断一个Collection是否为空， 包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Collection<?> coll) {
        return isNull(coll) || coll.isEmpty();
    }
    /**
     * * 判断一个Object是否为空
     *
     * @param object 要判断的对象
     * @return true：为空 false：非空
     */
    public static boolean isEmptyObject(Object object){
        return object == null || isEmpty(object.toString());
    }
    /**
     * * 判断一个Object是否不为空
     *
     * @param object 要判断的对象
     * @return true：不为空 false：为空
     */
    public static boolean isNotEmptyObject(Object object){
        return !isEmptyObject(object);
    }

    /**
     * * 判断一个Collection是否非空，包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }

    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     *                * @return true：为空 false：非空
     */
    public static boolean isEmpty(Object[] objects) {
        return isNull(objects) || (objects.length == 0);
    }

    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Object[] objects) {
        return !isEmpty(objects);
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return isNull(map) || map.isEmpty();
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * * 判断一个字符串是否为空串
     *
     * @param str String
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(String str) {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /**
     * * 判断一个字符串是否为非空串
     *
     * @param str String
     * @return true：非空串 false：空串
     */
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }

    /**
     * * 判断一个对象是否为空
     *
     * @param object Object
     * @return true：为空 false：非空
     */
    public static boolean isNull(Object object) {
        return object == null;
    }

    /**
     * * 判断一个对象是否非空
     *
     * @param object Object
     * @return true：非空 false：空
     */
    public static boolean isNotNull(Object object) {
        return !isNull(object);
    }

    /**
     * * 判断一个对象是否是数组类型（Java基本型别的数组）
     *
     * @param object 对象
     * @return true：是数组 false：不是数组
     */
    public static boolean isArray(Object object) {
        return isNotNull(object) && object.getClass().isArray();
    }

    /**
     * 去空格
     */
    public static String trim(String str) {
        return (str == null ? "" : str.trim());
    }

    /**
     * 截取字符串
     *
     * @param str   字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start) {
        if (str == null) {
            return NULLSTR;
        }

        if (start < 0) {
            start = str.length() + start;
        }

        if (start < 0) {
            start = 0;
        }
        if (start > str.length()) {
            return NULLSTR;
        }

        return str.substring(start);
    }

    /**
     * 截取字符串
     *
     * @param str   字符串
     * @param start 开始
     * @param end   结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end) {
        if (str == null) {
            return NULLSTR;
        }

        if (end < 0) {
            end = str.length() + end;
        }
        if (start < 0) {
            start = str.length() + start;
        }

        if (end > str.length()) {
            end = str.length();
        }

        if (start > end) {
            return NULLSTR;
        }

        if (start < 0) {
            start = 0;
        }
        if (end < 0) {
            end = 0;
        }

        return str.substring(start, end);
    }

    /**
     * 格式化文本, {} 表示占位符
     * 此方法只是简单将占位符 {} 按照顺序替换为参数
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
     * 例：
     * 通常使用：format("this is {} for {}", "a", "b") -> this is a for b
     * 转义{}： format("this is \\{} for {}", "a", "b") -> this is \{} for a
     * 转义\： format("this is \\\\{} for {}", "a", "b") -> this is \a for b
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param params   参数值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... params) {
        if (isEmpty(params) || isEmpty(template)) {
            return template;
        }
        return XFormatter.format(template, params);
    }


    /**
     * 是否包含字符串
     *
     * @param str  验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        if (str != null && strs != null) {
            for (String s : strs) {
                if (str.equalsIgnoreCase(trim(s))) {
                    return true;
                }
            }
        }
        return false;
    }


    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj) {
        return (T) obj;
    }

    public static String objToJson(Object obj) {
        return JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect);
    }

    /**
     * Restores a byte array that is encoded as a hex string.
     */
    public static byte[] hexStringToByteArray(String hexString) {
        if (hexString == null) {
            return null;
        }

        int length = hexString.length();
        byte[] bytes = new byte[length / 2];
        for (int i = 0; i < length; i += 2) {
            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return bytes;
    }

    public static boolean isBlank(CharSequence cs) {
        return StringUtils.isBlank(cs);
    }


    /**
     * user -> User
     */
    public static String getMethodName(String str) {
        char[] cs = str.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }


    /**
     * userName -> user_name
     *
     * @param name 字符串
     * @return String
     */
    public static String camelToUnderScoreCase(String name) {
        return camelToUnderScoreCase(name,true);
    }

    /**
     * userName -> user_name or userName -> USER_NAME
     *
     * @param name 字符串
     * @param isToLowerCase 是否需要转换成小写
     * @return String
     */
    public static String camelToUnderScoreCase(String name, boolean isToLowerCase) {
        if (name == null && name.equals(NULLSTR)) {
            return name;
        }
        String tempName = name.replaceAll("[A-Z]", "_$0");
        if (isToLowerCase) {
            return tempName.toLowerCase();
        }
        return tempName.toUpperCase();
    }


    /**
     * user_name -> userName
     */
    public static String underScoreCaseTocCamel(String name) {
        return underScoreCaseTocCamel(name, SEPARATOR);
    }

    /**
     * user_name -> userName
     */
    public static String underScoreCaseTocCamel(String name, String sp) {
        if (name == null && name.equals(NULLSTR)) {
            return name;
        }
        String[] partName = name.toLowerCase().split(sp);
        StringBuilder convertName = new StringBuilder();
        int index = 0;
        for (String str : partName) {
            convertName.append(index == 0 ? str : getMethodName(str));
            index++;
        }
        return convertName.toString();
    }


    public static Object ObjectConvert(Class<?> fieldType,Object val){
        try {
            if (val == null) {
                return null;
            }
            if (fieldType.isEnum()) {
                return toEnum(fieldType, val.toString());
            }
            switch (fieldType.getName()) {
                case "int":
                case "java.lang.Integer":
                    return Convert.toInt(val);
                case "java.lang.Double":
                case "double":
                    return Convert.toDouble(val);
                case "java.lang.Float":
                case "float":
                    return Convert.toFloat(val);
                case "java.util.Date":
                    return Convert.toDate(val);
                case "java.sql.Date":
                    return new Timestamp(Convert.toLong(val));
                case "boolean":
                case "java.lang.Boolean":
                    return toBoolean(val.toString());
                case "java.lang.Long":
                    return Convert.toLong(val);
                case "java.math.BigDecimal":
                    return Convert.toBigDecimal(val);
            }
            return val;
        }catch (Exception exception){
            throw new DalFrameException(ErrorCode.TYPE_CONVERT_ERROR,exception.getMessage());
        }
    }
    public static Object StringConvert(Class<?> fieldType,String val){
        if(isEmpty(val)){
            return null;
        }
        if(fieldType.isEnum()){
            return toEnum(fieldType,val);
        }
        switch (fieldType.getName()){
            case "int":
            case "java.lang.Integer":
                return Convert.toInt(val);
            case "java.lang.Double":
            case "double":
                return Convert.toDouble(val);
            case "java.lang.Float":
            case "float":
                return Convert.toFloat(val);
            case "java.util.Date":
                return Convert.toDate(val);
            case "java.sql.Date":
                return new Timestamp(Convert.toLong(val));
            case "boolean":
            case "java.lang.Boolean":
                return toBoolean(val);
            case "java.lang.Long":
                return Convert.toLong(val);
            case "java.math.BigDecimal":
                return Convert.toBigDecimal(val);
        }
        return val;
    }

    private static Object toEnum(Class<?> fieldType, String val) {
        try {
            if(isEmpty(val)){
                return null;
            }
            Object value = Enum.valueOf(fieldType.asSubclass(Enum.class), val.toString());
            return value;
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private static Boolean toBoolean(String val){
        if(isEmpty(val)){
            return false;
        }
        String[] keyArray = {"y","yes","open","1"};
        for(int i = 0; i < keyArray.length; i++){
            if(keyArray[i].equalsIgnoreCase(val.toString())){
                return true;
            }
        }
        return Convert.toBool(val);
    }


    public static BigDecimal toBigDecimal(String val){
        if(isEmpty(val)){
            return new BigDecimal(0);
        }
        try{
            return new BigDecimal(val);
        }catch(Exception e) {
            return new BigDecimal(0);
        }
    }


    /**
     * 对象转JSON
     * @param bean 对象
     * @return
     */
    public static String toJSONString(Object bean){
        return toJSONStringConfig(bean, null, null, null, null, null);
    }

    /**
     * 对象转JSON
     * @param bean 对象
     * @param excludeFilter 不序列化的字段
     * @param includeFilter 序列化字段范围
     * @param dateFormat 日期格式（如果只修改某个字段为特定格式使用valFunc处理），为空时返回默认
     * @param types 要处理值的数据类型
     * @param valFunc 处理返回值，参数：(对象，键，值) -> 处理后的值
     * @return
     */
    public static String toJSONString(Object bean, String[] excludeFilter, String[] includeFilter, String dateFormat, List<Type> types, ThreeParamFunction<Object, String, Object, Object> valFunc){
        return toJSONStringConfig(bean, excludeFilter, includeFilter, dateFormat, types, valFunc);
    }

    // 日期格式化时格式
    private static String DATE_FORMAT = null;
    // 默认日期格式
    private static final String DATE_FORMAT_FINAL = "yyyy-MM-dd HH:mm:ss";
    // 处理val值方法
    private static ThreeParamFunction<Object, String, Object, Object> VAL_FUNC = null;

    /**
     * 对象转JSON : Long转String，日期格式化，值处理，序列化字段处理
     * @param bean 对象
     * @param excludeFilter 不序列化的字段
     * @param includeFilter 序列化字段范围
     * @param dateFormat 日期格式（如果只修改某个字段为特定格式使用valFunc处理），为空时返回默认
     * @param types 要处理值的数据类型
     * @param valFunc 处理返回值，参数：(对象，键，值) -> 处理后的值
     * @return Long和日期全局修改，值只能修改第一层和Map中值(types为空不处理Map中的)
     */
    public static String toJSONStringConfig(Object bean, String[] excludeFilter, String[] includeFilter, String dateFormat, List<Type> types, ThreeParamFunction<Object, String, Object, Object> valFunc){
        // 必须加参数 否则键可能不一致 加false：否则字段可能与需要的不一致，加ture会使Config中加的数据不起作用
        SerializeConfig serializeConfig = new SerializeConfig(false);
        // Long转String  使用该方法会使直接转换时也将Long转为String
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        // 日期格式化  日期格式必须全局否则 只有第一次起作用，之后的会与第一次格式一样
        if(StringUtil.isNotEmpty(dateFormat)) {
            DATE_FORMAT = dateFormat;
            serializeConfig.put(Date.class, new JsonSerializer());
        }
        // 处理val值 map中的
        if(types != null && types.size() > 0 && valFunc != null) {
            VAL_FUNC = valFunc;
            JsonSerializerVal jsonSerializerVal = new JsonSerializerVal();
            for(Type type:types) {
                serializeConfig.put(type, jsonSerializerVal);
            }
        }
//        // 处理键  第一级的才能处理
//        if(keyFunc != null) {
//            NameFilter nameFilter = new NameFilter() {
//                @Override
//                public String process(Object obj, String key, Object val) {
//                    return keyFunc.apply(obj, key, val);
//                }
//            };
//            serializeConfig.addFilter(bean.getClass(),  nameFilter);
//        }
        // 处理值 第一级的才能处理 Map处理不了
        if(valFunc != null){
            ValueFilter valFilter = (obj, key, val) -> {
//            // 将Long型数据转为String
//            val = ObjectUtil.isNotEmpty(val) && val instanceof Long ? String.valueOf(val) : val;
//            // 处理时间格式
//            val = StringUtil.isNotEmpty(dateFormat) && ObjectUtil.isNotEmpty(val) && val instanceof Date ? new SimpleDateFormat(dateFormat).format(val) : val;
                // 执行自定义值规则
                return valFunc.apply(obj, key, val);
            };
            serializeConfig.addFilter(bean.getClass(), valFilter);
        }

        // 转JSON
        String ret = toJSONString(bean, serializeConfig, excludeFilter, includeFilter);

        // 恢复默认防止下一次处理JSON时格式与这次一样
        if(StringUtil.isNotEmpty(dateFormat)) {
            DATE_FORMAT = null;
//            serializeConfig.put(Date.class, new JsonSerializer());
        }
        if(valFunc != null) {
            VAL_FUNC = null;
//            if(types != null && types.size() > 0){
//                JsonSerializerVal jsonSerializerVal = new JsonSerializerVal();
//                for(Type type:types) {
//                    serializeConfig.put(type, jsonSerializerVal);
//                }
//            }
            serializeConfig.addFilter(bean.getClass(), (ValueFilter) (obj, key, val) -> val);
        }
//        if(keyFunc != null) {
//            serializeConfig.addFilter(bean.getClass(), (NameFilter) (obj, key, val) -> key);
//        }
        return ret;
    }

    /**
     * 获取JSON
     * @param bean 对象
     * @param serializeConfig config
     * @param excludeFilter 不序列化的字段
     * @param includeFilter 序列化字段范围
     * @return
     */
    public static String toJSONString(Object bean, SerializeConfig serializeConfig, String[] excludeFilter, String[] includeFilter){
        PropertyPreFilters filters = new PropertyPreFilters();
        PropertyPreFilters.MySimplePropertyPreFilter filter = filters.addFilter();
        // 序列化和不序列化的字段 否则 获取所有字段
        if((excludeFilter != null && excludeFilter.length > 0) || (includeFilter != null && includeFilter.length > 0)) {
            // 序列化排除的字段
            if(excludeFilter != null && excludeFilter.length > 0) {
                filter.addExcludes(excludeFilter);
            }
            // 只序列化的字段  返回json的key范围：includeFilter存在，excludeFilter不存在的
            if(includeFilter != null && includeFilter.length > 0) {
                filter.addIncludes(includeFilter);
            }
        }
        if(serializeConfig != null){
            // SerializerFeature.WriteMapNullValue:为null时保留  SerializerFeature.WriteDateUseDateFormat:日期格式化 DisableCircularReferenceDetect:禁止循环引用检测 SerializerFeature.PrettyFormat:格式化
            return JSON.toJSONString(bean, serializeConfig, filter, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
        }else{
            return JSON.toJSONString(bean, filter, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
        }
    }


    /**
     * 解析为Map时数据处理
     */
    static class JsonSerializer implements ObjectSerializer {
        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
            SerializeWriter out = serializer.getWriter();
            if (object == null) {
                serializer.getWriter().writeNull();
                return;
            }
            if(StringUtil.isNotEmpty(DATE_FORMAT)){
                // 格式必须全局 否则下一个格式转换不起作用
                SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
                // 对LocalDateTime类型进行格式转化
                out.write("\"" + sdf.format(object) + "\"");
            }else{
                // 还和原始一样返回时间戳
                out.write(((Date)object).getTime()+"");
            }
        }
    }

    /**
     * 自定义val 只有Map中字段才起作用
     */
    static class JsonSerializerVal implements ObjectSerializer {
        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
            SerializeWriter out = serializer.getWriter();
            if (object == null) {
                serializer.getWriter().writeNull();
                return;
            }
            String sub = out.toString().substring(0, out.toString().lastIndexOf("\""));
            String fn = sub.substring(sub.lastIndexOf("\"")+1);
            object = VAL_FUNC != null ? VAL_FUNC.apply(null, fn, object) : object;
            out.write("\"" + (String) object + "\"");
        }
    }

//    /**
//     * 对象转JSON : Long转String，日期格式化，字段处理，值处理，序列化字段处理--使用Filter只能修改第一层
//     * @param bean 对象
//     * @param excludeFilter 不序列化的字段
//     * @param includeFilter 序列化字段范围
//     * @param dateFormat 日期格式（如果只修改某个字段为特定格式使用valFunc处理），为空时返回标准事件格式
//     * @param keyFunc 处理字段名，参数：(对象，键，值) -> 处理后的值（不为null时beanClass不为空）
//     * @param valFunc 处理返回值，参数：(对象，键，值) -> 处理后的值（不为null时beanClass不为空）
//     * @return 方法结束后不影响JSON.toJSONString格式 Long和日期不能全部变成一样 Map不起作用
//     */
//    public static String toJSONStringFilter(Object bean, String[] excludeFilter, String[] includeFilter, String dateFormat, ThreeParamFunction<Object, String, Object, String> keyFunc, ThreeParamFunction<Object, String, Object, Object> valFunc){
//        if(bean == null){
//            return null;
//        }
//        // 必须加参数 否则键可能不一致 加false：否则字段可能与需要的不一致，加ture会使Config中加的数据不起作用
//        SerializeConfig serializeConfig = new SerializeConfig(false);
//        // 处理键
//        if(keyFunc != null) {
//            NameFilter nameFilter = new NameFilter() {
//                @Override
//                public String process(Object obj, String key, Object val) {
//                    return keyFunc.apply(obj, key, val);
//                }
//            };
//            serializeConfig.addFilter(bean.getClass(),  nameFilter);
//        }
//        // 处理值 第一级的才能处理 Map处理不了
//        ValueFilter valFilter = (obj, key, val) -> {
//            // 将Long型数据转为String
//            val = ObjectUtil.isNotEmpty(val) && val instanceof Long ? String.valueOf(val) : val;
//            // 处理时间格式
//            val = StringUtil.isNotEmpty(dateFormat) && ObjectUtil.isNotEmpty(val) && val instanceof Date ? new SimpleDateFormat(dateFormat).format(val) : val;
//            // 执行自定义值规则
//            return valFunc != null ? valFunc.apply(obj, key, val) : val;
//        };
//        serializeConfig.addFilter(bean.getClass(), valFilter);
//
//        // 转JSON
//        String ret = toJSONString(bean, serializeConfig, excludeFilter, includeFilter);
//
//        // 恢复默认防止下一次处理JSON时格式与这次一样
//        if(keyFunc != null) {
//            serializeConfig.addFilter(bean.getClass(), (NameFilter) (obj, key, val) -> key);
//        }
//        serializeConfig.addFilter(bean.getClass(), (ValueFilter) (obj, key, val) -> val);
//        return ret;
//    }
}
